/* Copyright Elysia © 2025. All rights reserved */

import { APIApplication, ApplicationFlags, GatewayIntentBits } from "discord-api-types/v10";
import { app, dialog } from "electron";
import { ApplicationFlagsBitField, IntentsBitField } from "src/AppUtils/DiscordBitField";
import { GuildExperiment, UserExperiment } from "src/AppUtils/Experiments";
import { fetch } from "undici";

import { DiscordBotClient } from ".";
import Constants from "./Constants";
import { IPCEvent } from "./IPCEvents";

export function setupIPCEvents (mainApp: DiscordBotClient) {
    const getWindow = (framename: string) => {
        return mainApp.childWindows.get(framename) ?? mainApp.win;
    };
    mainApp.ipcMain
        .on(IPCEvent.Minimize, (event, frameName) => {
            getWindow(frameName).minimize();
        })
        .on(IPCEvent.Maximize, (event, frameName) => {
            const win = getWindow(frameName);
            if (win.isMaximized()) {
                win.restore();
            } else {
                win.maximize();
            }
        })
        .on(IPCEvent.Close, (event, frameName) => {
            if (frameName) {
                return mainApp.childWindows.get(frameName)?.close();
            }
            mainApp.win.hide();
        })
        .on(IPCEvent.Focus, (event, frameName) => {
            const win = getWindow(frameName);
            // this.win.focus();
            win.show();
            win.setSkipTaskbar(false);
        });
    mainApp.ipcMain.handle(IPCEvent.GetBotInfo, (event, token) => {
        token = token.replace(/Bot/g, "").trim();
        return fetch("https://discord.com/api/v9/applications/@me?with_counts=true", {
            headers: {
                Authorization: `Bot ${token}`,
                "User-Agent": Constants.UserAgentDiscordBot,
            },
        })
            .then(res => {
                if (!res.ok) throw new Error(res.statusText);
                return res.json() as Promise<APIApplication>;
            })
            .then(data => {
                const applicationFlags = new ApplicationFlagsBitField(data.flags);
                const skipIntents = new Set<GatewayIntentBits>([
                    GatewayIntentBits.GuildPresences,
                    GatewayIntentBits.GuildMembers,
                    GatewayIntentBits.MessageContent,
                ]);
                if (
                    applicationFlags.has(ApplicationFlags.GatewayPresence) ||
                    applicationFlags.has(ApplicationFlags.GatewayPresenceLimited)
                ) {
                    skipIntents.delete(GatewayIntentBits.GuildPresences);
                }
                if (
                    applicationFlags.has(ApplicationFlags.GatewayGuildMembers) ||
                    applicationFlags.has(ApplicationFlags.GatewayGuildMembersLimited)
                ) {
                    skipIntents.delete(GatewayIntentBits.GuildMembers);
                }
                if (
                    applicationFlags.has(ApplicationFlags.GatewayMessageContent) ||
                    applicationFlags.has(ApplicationFlags.GatewayMessageContentLimited)
                ) {
                    skipIntents.delete(GatewayIntentBits.MessageContent);
                }
                if (
                    skipIntents.has(GatewayIntentBits.MessageContent) &&
                    !mainApp.config.config.suppress_intent_warning
                ) {
                    dialog.showErrorBox(
                        "MessageContent is required",
                        "You need to enable the MessageContent intent in the application settings.\nIf you want to permanently suppress this warning, change the application settings (accessible from the tray menu via right-click)",
                    );
                    throw new Error("MESSAGE_CONTENT is required.");
                }
                return {
                    success: true,
                    data,
                    intents: IntentsBitField.getIntents(...Array.from(skipIntents)),
                    allShards:
                        Math.ceil(
                            (data.approximate_guild_count ?? 0) / Number(mainApp.config.config.guilds_per_shard),
                        ) || 1,
                };
            })
            .catch(e => {
                return {
                    success: false,
                    message: e.message,
                };
            });
    });
    mainApp.ipcMain
        .on(IPCEvent.GetVersion, event => {
            return (event.returnValue = app.getVersion());
        })
        .on(IPCEvent.GetName, event => {
            return (event.returnValue = app.getName());
        })
        .on(IPCEvent.GetExperiment, (event, type, allData, botId) => {
            if (type === "user") {
                event.returnValue = UserExperiment(allData, botId);
            } else if (type === "guild") {
                event.returnValue = GuildExperiment();
            }
        })
        .on(IPCEvent.GetDefaultUserPatch, event => {
            event.returnValue = Constants.UserDefaultPatch;
        });
    // Config Editor
    mainApp.ipcMain.handle(IPCEvent.MonacoEditorGetConfig, event => {
        return mainApp.config.toString();
    });
    mainApp.ipcMain.handle(IPCEvent.MonacoEditorGetAutoComplete, event => {
        return mainApp.config.monacoAutoComplete();
    });
    mainApp.ipcMain.handle(IPCEvent.MonacoEditorSaveConfig, (event, config) => {
        // Validate and save the config
        try {
            mainApp.config.loadConfig(config);
            mainApp.config.save();
            mainApp.logger.info("Config saved successfully");
            return true;
        } catch (e) {
            mainApp.logger.error("Invalid config:", e);
            dialog.showErrorBox("Invalid Config", `The provided config is invalid: ${(e as Error).message}`);
            return false;
        }
    });
}
